package com.easy.framework.security.config;

import com.easy.framework.common.model.R;
import com.easy.framework.security.MyAuthenticationProvider;
import com.easy.framework.security.oauth2.OAuth2PasswordAuthenticationConverter;
import com.easy.framework.security.oauth2.OAuth2PasswordAuthenticationProvider;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.core.OAuth2AccessToken;
import org.springframework.security.oauth2.core.OAuth2RefreshToken;
import org.springframework.security.oauth2.jwt.JwtEncoder;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AccessTokenAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.OAuth2AuthorizationServerConfigurer;
import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;
import org.springframework.security.oauth2.server.authorization.token.*;
import org.springframework.security.oauth2.server.authorization.web.authentication.DelegatingAuthenticationConverter;
import org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2AuthorizationCodeRequestAuthenticationConverter;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.AuthenticationConverter;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.web.cors.CorsConfigurationSource;

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

@Log4j2
@Configuration
@RequiredArgsConstructor
public class AuthorizationServerConfig {

    private final JwtEncoder jwtEncoder;

    private final OAuth2AuthorizationService authorizationService;

    private final AuthenticationFailureHandler authenticationFailureHandler;

    @Autowired
    private CorsConfigurationSource corsConfigurationSource;

    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
        OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
        OAuth2AuthorizationServerConfigurer authorizationServerConfigurer = http.getConfigurer(OAuth2AuthorizationServerConfigurer.class);

        authorizationServerConfigurer
                .tokenEndpoint((tokenEndpoint) -> {
                    // 注入自定义的授权认证Converter
                    tokenEndpoint.accessTokenRequestConverter(accessTokenRequestConverter());
                    tokenEndpoint.accessTokenResponseHandler(accessTokenResponseHandler());
                    tokenEndpoint.errorResponseHandler(authenticationFailureHandler);
                })
                //redis存储token的实现
                .authorizationService(authorizationService)
                .authorizationServerSettings(AuthorizationServerSettings.builder()
                        .issuer("http://localhost:8080")
                        .tokenEndpoint("/auth/token")  // 登录接口
                        .authorizationEndpoint("/auth/authorize")  // 授权
                        .build());
//        http.cors(corsConfigurer -> corsConfigurer.disable());
        http.cors(corsConfigurer -> corsConfigurer.configurationSource(corsConfigurationSource));
        http.formLogin(Customizer.withDefaults());
        http.oauth2ResourceServer(resourceServer -> resourceServer.jwt(Customizer.withDefaults()));

        SecurityFilterChain filterChain = http.build();

        AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManager.class);
        http.authenticationProvider(new MyAuthenticationProvider());
        http.authenticationProvider(new OAuth2PasswordAuthenticationProvider(authenticationManager, authorizationService, oAuth2TokenGenerator()));

        return filterChain;
    }

    @Bean
    public AuthenticationSuccessHandler accessTokenResponseHandler() {
        return (request, response, authentication) -> {
            OAuth2AccessTokenAuthenticationToken accessTokenAuthentication =
                    (OAuth2AccessTokenAuthenticationToken) authentication;

            OAuth2AccessToken accessToken = accessTokenAuthentication.getAccessToken();
            OAuth2RefreshToken refreshToken = accessTokenAuthentication.getRefreshToken();

            Map<String, Object> responseData = new LinkedHashMap<>();
            responseData.put("access_token", accessToken.getTokenValue());
            responseData.put("token_type", accessToken.getTokenType().getValue());
            responseData.put("expires_in", accessToken.getExpiresAt().getEpochSecond());

            if (refreshToken != null) {
                responseData.put("refresh_token", refreshToken.getTokenValue());
            }

            response.setContentType(MediaType.APPLICATION_JSON_VALUE);
            new ObjectMapper().writeValue(response.getWriter(), R.success(responseData));
        };
    }

    private AuthenticationConverter accessTokenRequestConverter() {
        return new DelegatingAuthenticationConverter(
                List.of(
                        new OAuth2PasswordAuthenticationConverter()
                        , new OAuth2AuthorizationCodeRequestAuthenticationConverter()
                ));
    }


    /**
     * 令牌生成规则实现 </br>
     * client:username:uuid
     *
     * @return OAuth2TokenGenerator
     */
    @Bean
    public OAuth2TokenGenerator oAuth2TokenGenerator() {
        JwtGenerator jwtGenerator = new JwtGenerator(jwtEncoder);
        return new DelegatingOAuth2TokenGenerator(
                //OpenId生成支持
                jwtGenerator,
                //AccessToken令牌生成
                new OAuth2AccessTokenGenerator(),
                //RefreshToken令牌生成
                new OAuth2RefreshTokenGenerator()
        );
    }

}